package com.example.easyexceldemo.controller;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Workbook;
import org.easy.excel.ExcelContext;
import org.easy.excel.exception.ExcelDataException;
import org.easy.excel.exception.ExcelException;
import org.easy.excel.result.ExcelExportResult;
import org.easy.excel.result.ExcelImportResult;
import org.easy.excel.util.ExcelDownLoadUtil;
import org.easy.excel.util.ExcelUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import com.example.easyexceldemo.model.Student;
import com.example.easyexceldemo.service.StudentService;
import com.example.easyexceldemo.util.ResponseResult;

import lombok.SneakyThrows;

@RestController
public class DemoController {

	@Autowired
	private ExcelContext excelContext;

	@Autowired
	private StudentService studentService;

	private String excelId = "student";
	
	//导出薪资列表
	@GetMapping("/exportSalary")
	@SneakyThrows
	public void exportTemplate(HttpServletRequest request, HttpServletResponse response) {
		List<Student> all = studentService.getAll();
		long start = System.currentTimeMillis();
		//指定只导出name和salary字段
		List<String> fieldNames = new ArrayList<>();
		fieldNames.add("name");
		fieldNames.add("salary");
		Workbook excel = excelContext.createExcel(excelId, all, null, fieldNames);
		ExcelDownLoadUtil.downLoadExcel(excel, "学生薪资信息", "没有可导出的列表数据", request, response);
		System.out.println("耗时:ms"+(System.currentTimeMillis() - start));
	}

	// 一次性导出
	@GetMapping("/export")
	@SneakyThrows
	public void exportAll(HttpServletRequest request, HttpServletResponse response) {
		List<Student> all = studentService.getAll();
		long start = System.currentTimeMillis();
		Workbook excel = excelContext.createExcel(excelId, all);
		ExcelDownLoadUtil.downLoadExcel(excel, "学生信息明细", "没有可导出的列表数据", request, response);
		System.out.println("耗时:ms"+(System.currentTimeMillis() - start));
	}

	// 查库分页分批导出
	@GetMapping("/exportPage")
	@SneakyThrows
	public void exportPage(HttpServletRequest request, HttpServletResponse response) {
		int pageNo = 1;// 从第一页开始
		int pageSize = 5000;// 每次查询5000条数据
		List<Student> list = studentService.page(pageNo, pageSize);
		ExcelExportResult result = null;
		while (!list.isEmpty()) {
			pageNo++;// 下一页
			if (result == null) {
				// 第一次添加数据
				result = excelContext.createExcelForPart(excelId, list);
			} else {
				// 追加非第一次查询数据库的数据
				result.append(list);
			}
			list = studentService.page(pageNo, pageSize);
		}
		Workbook excel = result != null ? result.build() : null;
		ExcelDownLoadUtil.downLoadExcel(excel, "学生信息明细", "没有可导出的列表数据", request, response);
	}

	// 多行校验，字符串提示方式
	@PostMapping("/importMulti")
	@SneakyThrows
	public ResponseResult<String> importMulti(@RequestParam(name = "file") MultipartFile file) {
		long start = System.currentTimeMillis();
		try {
			ExcelImportResult result = excelContext.readExcel(excelId, 0, file.getInputStream(), true);
			// 如果是批量操作可以拿着validListBean入库,当然对于其他的业务逻辑校验需要从业务方去实现,比如数据去重等...
			List<Student> validListBean = result.getValidListBean();
			System.out.println("校验通过的数据条数为:" + validListBean.size());
			System.out.println("校验为通过的数据条数为:" + result.getErrorListBean().size());
			System.out.println("获取全量数据条数方式1:" + result.getTotalNum());
			System.out.println("获取全量数据条数方式2:" + result.getListBean().size());
			if (result.hasErrors()) {
				List<String> errors = processColumnLevelErrors(result);
				//多个错误信息拼接成一个字符串
				String str = StringUtils.join(errors, ",");
				return ResponseResult.failure(1, str);
			}
		} catch (ExcelException e) {
			return ResponseResult.failure(2, e.getMessage());
		}finally {
			System.out.println("耗时:ms"+(System.currentTimeMillis() - start));
		}
		return ResponseResult.success();
	}

	// 单列校验,任何一条数据发生错误停止本次操作给出错误提示
	@PostMapping("/import")
	@SneakyThrows
	public ResponseResult<String> importExcel(@RequestParam(name = "file") MultipartFile file) {
		try {
			ExcelImportResult result = excelContext.readExcel(excelId, 0, file.getInputStream(), false);
			// 所以数据校验通过才会执行到这一行,任何校验不通过都会被下面的ExcelException处理
			System.out.println("处理数据条数为:" + result.getListBean().size());
		} catch (ExcelException e) {
			return ResponseResult.failure(2, e.getMessage());
		}
		return ResponseResult.success();
	}

	private List<String> processColumnLevelErrors(ExcelImportResult result) {
		if (result.hasErrors()) {
			List<String> rowErrList = new ArrayList<>();
			for (ExcelDataException e : result.getErrors()) {
				rowErrList.add(e.getMessage());
			}
			return rowErrList;
		}
		return null;
	}
	
	//单独把错误信息独立拿出来同时生成一个新的excel
	
	//生成一个错误的excel
	@PostMapping("/importMulti2")
	@SneakyThrows
	public void importMulti2(@RequestParam(name = "file") MultipartFile file,HttpServletRequest request, HttpServletResponse response) {
		ExcelImportResult result = excelContext.readExcel(excelId, 0, file.getInputStream(), true);
		if(result.hasErrors()) {
			Workbook workbook = ExcelUtil.getErrorWorkbook(result.getErrors(), 6000);
			ExcelDownLoadUtil.downLoadExcel(workbook, "导入错误描述文件", "", request, response);
		}
	}
	

	// 处理行级别的错误提示，这种情况适合不关心行号,但是需要把同一行的错误信息拼接成同一行
	private <T> Map<T, String> processRowLevelErrors(ExcelImportResult result) {
		if (result.hasErrors()) {
			Map<T, String> map = new HashMap<>();
			for (ExcelDataException e : result.getErrors()) {
				@SuppressWarnings("unchecked")
				T refObject = (T) e.getRefObject();
				if (map.containsKey(refObject)) {
					String str = map.get(refObject);
					str += ",[" + e.getTitle() + "]" + e.getErrInfo();
					map.put(refObject, str);
				} else {
					map.put(refObject, "[" + e.getTitle() + "]" + e.getErrInfo());
				}
			}
			return map;
		}
		return null;
	}

}
